package com.lan.workflow.controller;

import com.lan.workflow.annotation.Log;
import com.lan.workflow.image.CustomProcessDiagramGenerator;
import com.lan.workflow.service.IActivitiService;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.impl.RepositoryServiceImpl;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.pvm.PvmTransition;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.repository.ProcessDefinitionQuery;
import org.activiti.engine.runtime.ProcessInstance;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.*;


@RequestMapping("/activiti")
@RestController
public class ActivitiController {

    private static Logger logger = LoggerFactory.getLogger(ActivitiController.class);
    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private FormService formService;
    @Autowired
    private IdentityService identityService;
    @Autowired
    private TaskService taskService;
    @Autowired
    private HistoryService historyService;
    @Autowired
    private CustomProcessDiagramGenerator customProcessDiagramGenerator;

    @Autowired
    private IActivitiService activitiService;

    private BpmnXMLConverter bpmnXmlConverter = new BpmnXMLConverter();
    private BpmnJsonConverter bpmnJsonConverter = new BpmnJsonConverter();

    @Log
    @PostMapping(value = "/approval/{instanceId}", produces = {"application/json;charset=utf-8;"})
    public Map<String, Object> approval(@PathVariable("instanceId") String instanceId, @RequestBody Map<String, Object> requestMap) throws Exception {
        Map<String, Object> result = new HashMap<>();
        /*Subject subject = SecurityUtils.getSubject();
        Object obj = subject.getPrincipal();
        if (obj == null) {
            result.put("code", ResponseCodeEnum.BUSINESS_ERROR.getKey());
            result.put("msg", "您没有权限审核");
        } else {
            User user = new User();
            BeanUtils.copyBeanProp(user, obj);*/
            result = activitiService.approval(instanceId, requestMap, "000003");
        //}
        return result;
    }

    @RequestMapping(value = "diagram/{processInstanceId}", method = RequestMethod.GET)
    public void diagram(@PathVariable("processInstanceId") String processInstanceId, HttpServletResponse response) throws Exception {
        long start = System.currentTimeMillis();
        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        ProcessDefinition processDefinition;
        List<String> highLightedActivitiIds = new ArrayList<>();
        // 获得历史活动记录实体（通过启动时间正序排序，不然有的线可以绘制不出来）
        List<HistoricActivityInstance> historicActivityInstances = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(processInstanceId)
                .orderByHistoricActivityInstanceId().asc().list();
        if(processInstance == null){
            //历史流程/已结束的流程    比较复杂的流程图不建议显示执行流程，高亮线条流程等 看起来会比较乱
            HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
            //已结束流程查询
            processDefinition = processDefinitionQuery.processDefinitionId(historicProcessInstance.getProcessDefinitionId()).singleResult();
            // 高亮已经执行流程节点ID集合
            for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) {
                highLightedActivitiIds.add(historicActivityInstance.getActivityId());
            }
        }else{
            //正在执行的流程查询
            processDefinition = processDefinitionQuery.processDefinitionId(processInstance.getProcessDefinitionId()).singleResult();
            highLightedActivitiIds = runtimeService.getActiveActivityIds(processInstanceId);
        }

        BpmnModel bpmnmodel = repositoryService.getBpmnModel(processDefinition.getId());
        //CustomProcessDiagramGenerator diagramGenerator = new CustomProcessDiagramGenerator();
        //DefaultProcessDiagramGenerator diagramGenerator = new DefaultProcessDiagramGenerator();
        // 计算活动线
        List<String> highLightedFlows = getHighLightedFlows(
                (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService).getDeployedProcessDefinition(processDefinition.getId()),
                        historicActivityInstances);

        InputStream in = customProcessDiagramGenerator.generateDiagram(bpmnmodel, "png", highLightedActivitiIds, highLightedFlows, "黑体", "黑体", "黑体",
                null, 0.8);
        OutputStream output = response.getOutputStream();
        FileCopyUtils.copy(in, output);
        /*Map<String, Object> result = new HashMap<>();
        result.put("code", ResponseCodeEnum.SUCCESS.getKey());
        result.put("msg", ResponseCodeEnum.SUCCESS.getValue());
        result.put("data",new String(Base64.getEncoder().encode(FileCopyUtils.copyToByteArray(in))));*/
        logger.info("生产图片耗时：{}", System.currentTimeMillis() - start);
        //return result;
    }


    public List<String> getHighLightedFlows(ProcessDefinitionEntity processDefinitionEntity,
                                            List<HistoricActivityInstance> historicActivityInstances) {

        List<String> highFlows = new ArrayList<String>();// 用以保存高亮的线flowId
        for (int i = 0; i < historicActivityInstances.size(); i++) {// 对历史流程节点进行遍历
            ActivityImpl activityImpl = processDefinitionEntity
                    .findActivity(historicActivityInstances.get(i)
                            .getActivityId());// 得 到节点定义的详细信息
            List<ActivityImpl> sameStartTimeNodes = new ArrayList<ActivityImpl>();// 用以保存后需开始时间相同的节点
            if ((i + 1) >= historicActivityInstances.size()) {
                break;
            }
            ActivityImpl sameActivityImpl1 = processDefinitionEntity
                    .findActivity(historicActivityInstances.get(i + 1)
                            .getActivityId());// 将后面第一个节点放在时间相同节点的集合里
            sameStartTimeNodes.add(sameActivityImpl1);
            for (int j = i + 1; j < historicActivityInstances.size() - 1; j++) {
                HistoricActivityInstance activityImpl1 = historicActivityInstances
                        .get(j);// 后续第一个节点
                HistoricActivityInstance activityImpl2 = historicActivityInstances
                        .get(j + 1);// 后续第二个节点
                if (activityImpl1.getStartTime().equals(
                        activityImpl2.getStartTime())) {// 如果第一个节点和第二个节点开始时间相同保存
                    ActivityImpl sameActivityImpl2 = processDefinitionEntity
                            .findActivity(activityImpl2.getActivityId());
                    sameStartTimeNodes.add(sameActivityImpl2);
                } else {// 有不相同跳出循环
                    break;
                }
            }
            List<PvmTransition> pvmTransitions = activityImpl
                    .getOutgoingTransitions();// 取出节点的所有出去的线
            for (PvmTransition pvmTransition : pvmTransitions) {// 对所有的线进行遍历
                ActivityImpl pvmActivityImpl = (ActivityImpl) pvmTransition
                        .getDestination();// 如果取出的线的目标节点存在时间相同的节点里，保存该线的id，进行高亮显示
                if (sameStartTimeNodes.contains(pvmActivityImpl)) {
                    highFlows.add(pvmTransition.getId());
                }
            }
        }
        return highFlows;
    }

}
